import streamlit as st
import redis
import json
import time
import pandas as pd
from datetime import datetime, timedelta
import re
import uuid
import base64
import io
import traceback
import altair as alt


def log_action(action, detail):
    log_entry = {
        "time": datetime.now().isoformat(),
        "action": action,
        "detail": detail
    }
    # 存到Redis的list
    st.session_state.redis_client.lpush("admin:logs", json.dumps(log_entry))


# 设置页面配置
st.set_page_config(
    page_title="Redis 可视化管理工具",
    layout="wide",
    initial_sidebar_state="expanded",
    menu_items={
        'About': "# Redis 可视化管理工具\n由Streamlit强力驱动的Redis管理界面"
    }
)

# 自定义CSS样式
st.markdown("""
<style>
    .main-header {
        font-size: 2.5rem;
        color: #FF4B4B;
        margin-bottom: 1rem;
    }
    .sub-header {
        font-size: 1.5rem;
        color: #4B77BE;
        margin-top: 1.5rem;
        margin-bottom: 0.5rem;
    }
    .key-type {
        font-size: 0.9rem;
        color: #6c757d;
        font-style: italic;
    }
    .key-ttl {
        font-size: 0.9rem;
        color: #28a745;
    }
    .key-expired {
        font-size: 0.9rem;
        color: #dc3545;
    }
    .stButton button {
        width: 100%;
    }
    .connection-status-ok {
        color: #28a745;
        font-weight: bold;
    }
    .connection-status-error {
        color: #dc3545;
        font-weight: bold;
    }
    .data-container {
        background-color: #f8f9fa;
        padding: 1rem;
        border-radius: 0.5rem;
        margin-bottom: 1rem;
    }
    .info-box {
        background-color: #e9ecef;
        padding: 0.5rem;
        border-radius: 0.3rem;
        margin-bottom: 0.5rem;
    }
    .warning-text {
        color: #dc3545;
        font-weight: bold;
    }
    .success-text {
        color: #28a745;
        font-weight: bold;
    }
</style>
""", unsafe_allow_html=True)

# 初始化会话状态
if 'connections' not in st.session_state:
    st.session_state.connections = {}  # 存储连接配置
if 'current_connection' not in st.session_state:
    st.session_state.current_connection = None  # 当前连接ID
if 'redis_client' not in st.session_state:
    st.session_state.redis_client = None  # Redis客户端实例
if 'keys_pattern' not in st.session_state:
    st.session_state.keys_pattern = "*"  # 键搜索模式
if 'page_number' not in st.session_state:
    st.session_state.page_number = 0  # 当前页码
if 'page_size' not in st.session_state:
    st.session_state.page_size = 50  # 每页显示数量
if 'total_keys' not in st.session_state:
    st.session_state.total_keys = 0  # 键总数
if 'filtered_keys' not in st.session_state:
    st.session_state.filtered_keys = []  # 过滤后的键列表
if 'show_system_info' not in st.session_state:
    st.session_state.show_system_info = False  # 是否显示系统信息
# 标题
st.markdown('<h1 class="main-header">🔧 Redis 可视化管理工具</h1>', unsafe_allow_html=True)
st.caption("作者：何双新 ｜ Redis + 可视化工具")
# 侧边栏 - 连接管理
with st.sidebar:
    st.header("连接管理")

    # 连接配置表单
    with st.expander("创建新连接", expanded=True if not st.session_state.connections else False):
        with st.form("connection_form"):
            conn_name = st.text_input("连接名称", value="本地Redis")
            host = st.text_input("主机名", value="localhost")
            port = st.number_input("端口", value=6379, step=1)
            db = st.number_input("数据库编号 (db)", value=0, step=1, min_value=0)
            password = st.text_input("密码", type="password")
            use_ssl = st.checkbox("使用SSL/TLS")
            timeout = st.number_input("连接超时(秒)", value=5, step=1, min_value=1)

            submitted = st.form_submit_button("保存连接")
            if submitted:
                conn_id = str(uuid.uuid4())
                st.session_state.connections[conn_id] = {
                    "name": conn_name,
                    "host": host,
                    "port": port,
                    "db": db,
                    "password": password,
                    "use_ssl": use_ssl,
                    "timeout": timeout
                }
                st.success(f"连接 '{conn_name}' 已保存")

    # 已保存的连接列表
    if st.session_state.connections:
        st.subheader("已保存的连接")
        for conn_id, conn in st.session_state.connections.items():
            col1, col2, col3 = st.columns([3, 1, 1])
            with col1:
                if st.button(f"📌 {conn['name']}", key=f"conn_{conn_id}"):
                    try:
                        # 创建Redis连接
                        client_kwargs = {
                            "host": conn["host"],
                            "port": conn["port"],
                            "db": conn["db"],
                            "decode_responses": True,
                            "socket_timeout": conn["timeout"]
                        }

                        if conn["password"]:
                            client_kwargs["password"] = conn["password"]

                        if conn["use_ssl"]:
                            client_kwargs["ssl"] = True
                            client_kwargs["ssl_cert_reqs"] = None

                        redis_client = redis.Redis(**client_kwargs)
                        redis_client.ping()  # 测试连接

                        # 更新会话状态
                        st.session_state.redis_client = redis_client
                        st.session_state.current_connection = conn_id
                        st.session_state.keys_pattern = "*"
                        st.session_state.page_number = 0
                        st.success(f"✅ 成功连接到 {conn['name']}")
                    except Exception as e:
                        st.error(f"❌ 连接失败: {str(e)}")

            with col2:
                if st.button("🗑️", key=f"del_{conn_id}"):
                    if st.session_state.current_connection == conn_id:
                        st.session_state.redis_client = None
                        st.session_state.current_connection = None
                    del st.session_state.connections[conn_id]
                    st.rerun()

            with col3:
                if st.button("✏️", key=f"edit_{conn_id}"):
                    st.session_state.edit_connection = conn_id
                    st.rerun()

    # 编辑连接
    if 'edit_connection' in st.session_state and st.session_state.edit_connection:
        conn_id = st.session_state.edit_connection
        conn = st.session_state.connections[conn_id]

        st.subheader(f"编辑连接: {conn['name']}")
        with st.form("edit_connection_form"):
            conn_name = st.text_input("连接名称", value=conn["name"])
            host = st.text_input("主机名", value=conn["host"])
            port = st.number_input("端口", value=conn["port"], step=1)
            db = st.number_input("数据库编号 (db)", value=conn["db"], step=1, min_value=0)
            password = st.text_input("密码", value=conn["password"], type="password")
            use_ssl = st.checkbox("使用SSL/TLS", value=conn["use_ssl"])
            timeout = st.number_input("连接超时(秒)", value=conn.get("timeout", 5), step=1, min_value=1)

            col1, col2 = st.columns(2)
            with col1:
                if st.form_submit_button("保存"):
                    st.session_state.connections[conn_id] = {
                        "name": conn_name,
                        "host": host,
                        "port": port,
                        "db": db,
                        "password": password,
                        "use_ssl": use_ssl,
                        "timeout": timeout
                    }
                    del st.session_state.edit_connection
                    st.rerun()

            with col2:
                if st.form_submit_button("取消"):
                    del st.session_state.edit_connection
                    st.rerun()

    # 主题切换
    st.sidebar.subheader("设置")

    # 显示系统信息
    if st.sidebar.checkbox("显示Redis服务器信息", value=st.session_state.show_system_info):
        st.session_state.show_system_info = True
    else:
        st.session_state.show_system_info = False

    # 只读模式
    st.session_state.readonly = st.sidebar.checkbox("只读模式", value=st.session_state.get("readonly", False))

# 主界面
if st.session_state.redis_client is None:
    st.info("👈 请在侧边栏中创建或选择一个Redis连接")
else:
    try:
        # 写操作前判断
        if st.session_state.readonly:
            st.warning("当前为只读模式，无法进行写操作。")
            st.stop()

        # 获取当前连接信息
        current_conn = st.session_state.connections[st.session_state.current_connection]

        # 显示连接信息
        conn_info = f"**当前连接**: {current_conn['name']} ({current_conn['host']}:{current_conn['port']}/{current_conn['db']})"
        st.markdown(f'<p class="connection-status-ok">{conn_info}</p>', unsafe_allow_html=True)

        # 显示Redis服务器信息
        if st.session_state.show_system_info:
            with st.expander("Redis服务器信息", expanded=True):
                try:
                    info = st.session_state.redis_client.info()

                    # 创建信息表格
                    col1, col2 = st.columns(2)
                    with col1:
                        st.markdown("### 服务器信息")
                        server_info = {
                            "Redis版本": info.get("redis_version", "未知"),
                            "操作系统": info.get("os", "未知"),
                            "进程ID": info.get("process_id", "未知"),
                            "运行时间": f"{info.get('uptime_in_days', 0)}天 {info.get('uptime_in_seconds', 0) % (24 * 3600) // 3600}小时",
                            "连接客户端数": info.get("connected_clients", 0),
                            "内存使用": f"{info.get('used_memory_human', '0B')} / {info.get('maxmemory_human', '不限')}"
                        }

                        for key, value in server_info.items():
                            st.markdown(f"**{key}**: {value}")

                    with col2:
                        st.markdown("### 数据库信息")
                        keyspace_info = {}
                        for key, value in info.items():
                            if key.startswith("db"):
                                db_number = key[2:]
                                keyspace_info[
                                    f"数据库 {db_number}"] = f"键数量: {value.get('keys', 0)}, 过期键: {value.get('expires', 0)}"

                        if keyspace_info:
                            for key, value in keyspace_info.items():
                                st.markdown(f"**{key}**: {value}")
                        else:
                            st.markdown("无数据库信息")

                    # 显示性能指标
                    st.markdown("### 性能指标")
                    col1, col2, col3, col4 = st.columns(4)
                    with col1:
                        st.metric("每秒操作数", info.get("instantaneous_ops_per_sec", 0))
                    with col2:
                        st.metric("命中率",
                                  f"{info.get('keyspace_hits', 0) / (info.get('keyspace_hits', 0) + info.get('keyspace_misses', 1)) * 100:.2f}%")
                    with col3:
                        st.metric("内存碎片率", info.get("mem_fragmentation_ratio", 0))
                    with col4:
                        st.metric("已连接客户端", info.get("connected_clients", 0))

                except Exception as e:
                    st.error(f"获取服务器信息失败: {str(e)}")

        # 键空间浏览
        st.markdown('<h2 class="sub-header">📦 Redis 键空间浏览</h2>', unsafe_allow_html=True)

        # 搜索和过滤
        col1, col2, col3 = st.columns([3, 1, 1])
        with col1:
            keys_pattern = st.text_input("键名搜索模式", value=st.session_state.keys_pattern)
            if keys_pattern != st.session_state.keys_pattern:
                st.session_state.keys_pattern = keys_pattern
                st.session_state.page_number = 0

        with col2:
            key_type_filter = st.selectbox(
                "按类型过滤",
                ["全部", "string", "list", "hash", "set", "zset"],
                index=0
            )

        with col3:
            refresh = st.button("🔄 刷新", use_container_width=True)
            if refresh:
                st.session_state.page_number = 0

        # 获取键列表
        try:
            # 获取匹配的键
            all_keys = st.session_state.redis_client.keys(st.session_state.keys_pattern)

            # 按类型过滤
            if key_type_filter != "全部":
                filtered_keys = []
                for key in all_keys:
                    if st.session_state.redis_client.type(key) == key_type_filter:
                        filtered_keys.append(key)
            else:
                filtered_keys = all_keys

            # 更新会话状态
            st.session_state.total_keys = len(all_keys)
            st.session_state.filtered_keys = filtered_keys

            # 分页
            total_pages = (len(filtered_keys) + st.session_state.page_size - 1) // st.session_state.page_size

            if total_pages > 0:
                # 分页控件
                col1, col2, col3, col4, col5 = st.columns([1, 3, 2, 3, 1])
                with col1:
                    if st.button("⏮️ 首页", disabled=st.session_state.page_number == 0):
                        st.session_state.page_number = 0
                        st.rerun()

                with col2:
                    if st.button("⏪ 上一页", disabled=st.session_state.page_number == 0):
                        st.session_state.page_number = max(0, st.session_state.page_number - 1)
                        st.rerun()

                with col3:
                    st.markdown(f"**第 {st.session_state.page_number + 1} / {total_pages} 页**")

                with col4:
                    if st.button("下一页 ⏩", disabled=st.session_state.page_number >= total_pages - 1):
                        st.session_state.page_number = min(total_pages - 1, st.session_state.page_number + 1)
                        st.rerun()

                with col5:
                    if st.button("尾页 ⏭️", disabled=st.session_state.page_number >= total_pages - 1):
                        st.session_state.page_number = total_pages - 1
                        st.rerun()

                # 计算当前页的键
                start_idx = st.session_state.page_number * st.session_state.page_size
                end_idx = min(start_idx + st.session_state.page_size, len(filtered_keys))
                current_page_keys = filtered_keys[start_idx:end_idx]

                # 显示键信息
                st.markdown(f"显示 **{len(filtered_keys)}** 个键中的第 **{start_idx + 1}** 到 **{end_idx}** 个")

                # 批量操作
                with st.expander("批量操作"):
                    col1, col2, col3 = st.columns(3)
                    with col1:
                        if st.button("批量删除选中键", use_container_width=True):
                            keys_to_delete = st.session_state.get('selected_keys', [])
                            if keys_to_delete:
                                if st.session_state.redis_client.delete(*keys_to_delete):
                                    st.success(f"成功删除 {len(keys_to_delete)} 个键")
                                    # 清除选择并刷新
                                    st.session_state.selected_keys = []
                                    st.rerun()
                                else:
                                    st.error("删除操作失败")
                            else:
                                st.warning("请先选择要删除的键")

                    with col2:
                        if st.button("导出选中键", use_container_width=True):
                            keys_to_export = st.session_state.get('selected_keys', [])
                            if keys_to_export:
                                export_data = {}
                                for key in keys_to_export:
                                    key_type = st.session_state.redis_client.type(key)
                                    if key_type == "string":
                                        export_data[key] = {
                                            "type": key_type,
                                            "value": st.session_state.redis_client.get(key)
                                        }
                                    elif key_type == "list":
                                        export_data[key] = {
                                            "type": key_type,
                                            "value": st.session_state.redis_client.lrange(key, 0, -1)
                                        }
                                    elif key_type == "hash":
                                        export_data[key] = {
                                            "type": key_type,
                                            "value": st.session_state.redis_client.hgetall(key)
                                        }
                                    elif key_type == "set":
                                        export_data[key] = {
                                            "type": key_type,
                                            "value": list(st.session_state.redis_client.smembers(key))
                                        }
                                    elif key_type == "zset":
                                        export_data[key] = {
                                            "type": key_type,
                                            "value": st.session_state.redis_client.zrange(key, 0, -1, withscores=True)
                                        }

                                # 转换为JSON并提供下载
                                json_str = json.dumps(export_data, indent=2, ensure_ascii=False)
                                b64 = base64.b64encode(json_str.encode()).decode()
                                href = f'<a href="data:application/json;base64,{b64}" download="redis_export.json">点击下载JSON文件</a>'
                                st.markdown(href, unsafe_allow_html=True)
                            else:
                                st.warning("请先选择要导出的键")

                    with col3:
                        if st.button("设置过期时间", use_container_width=True):
                            keys_to_expire = st.session_state.get('selected_keys', [])
                            if keys_to_expire:
                                expiry_seconds = st.number_input("过期时间(秒)", min_value=1, value=3600)
                                if st.button("确认设置"):
                                    success_count = 0
                                    for key in keys_to_expire:
                                        if st.session_state.redis_client.expire(key, expiry_seconds):
                                            success_count += 1

                                    if success_count > 0:
                                        st.success(f"成功为 {success_count} 个键设置过期时间")
                                        st.rerun()
                                    else:
                                        st.error("设置过期时间失败")
                            else:
                                st.warning("请先选择要设置过期时间的键")

                # 创建键选择状态
                if 'selected_keys' not in st.session_state:
                    st.session_state.selected_keys = []

                # 显示键列表
                key_data = []
                for key in current_page_keys:
                    key_type = st.session_state.redis_client.type(key)
                    ttl = st.session_state.redis_client.ttl(key)
                    ttl_str = "永久" if ttl == -1 else f"{ttl}秒" if ttl >= 0 else "已过期"

                    # 获取键大小
                    try:
                        if key_type == "string":
                            size = len(st.session_state.redis_client.get(key) or "")
                        elif key_type == "list":
                            size = st.session_state.redis_client.llen(key)
                        elif key_type == "hash":
                            size = st.session_state.redis_client.hlen(key)
                        elif key_type == "set":
                            size = st.session_state.redis_client.scard(key)
                        elif key_type == "zset":
                            size = st.session_state.redis_client.zcard(key)
                        else:
                            size = 0
                    except:
                        size = 0

                    key_data.append({
                        "选择": key in st.session_state.selected_keys,
                        "键名": key,
                        "类型": key_type,
                        "大小": size,
                        "过期时间": ttl_str
                    })

                # 创建DataFrame
                df = pd.DataFrame(key_data)

                # 使用st.data_editor显示表格并允许选择
                edited_df = st.data_editor(
                    df,
                    column_config={
                        "选择": st.column_config.CheckboxColumn(
                            "选择",
                            help="选择要操作的键",
                            default=False,
                        ),
                        "键名": st.column_config.TextColumn(
                            "键名",
                            help="Redis键名",
                            width="large",
                        ),
                        "类型": st.column_config.TextColumn(
                            "类型",
                            help="Redis数据类型",
                        ),
                        "大小": st.column_config.NumberColumn(
                            "大小",
                            help="元素数量或字符串长度",
                            format="%d",
                        ),
                        "过期时间": st.column_config.TextColumn(
                            "过期时间",
                            help="键的过期时间",
                        ),
                    },
                    hide_index=True,
                    use_container_width=True,
                    disabled=["键名", "类型", "大小", "过期时间"],
                    key="keys_table"
                )

                # 更新选中的键
                selected_keys = []
                for i, row in edited_df.iterrows():
                    if row["选择"]:
                        selected_keys.append(row["键名"])
                st.session_state.selected_keys = selected_keys

                # 显示选中的键数量
                if selected_keys:
                    st.info(f"已选择 {len(selected_keys)} 个键")

                # 键详情查看
                st.markdown('<h2 class="sub-header">🔍 键详情查看</h2>', unsafe_allow_html=True)

                selected_key = st.selectbox("选择要查看的键", ["请选择..."] + current_page_keys)

                if selected_key and selected_key != "请选择...":
                    key_type = st.session_state.redis_client.type(selected_key)
                    ttl = st.session_state.redis_client.ttl(selected_key)

                    # 显示键信息
                    col1, col2, col3 = st.columns(3)
                    with col1:
                        st.markdown(f"**键名**: `{selected_key}`")
                    with col2:
                        st.markdown(f"**类型**: `{key_type}`")
                    with col3:
                        ttl_str = "永久" if ttl == -1 else f"{ttl}秒" if ttl >= 0 else "已过期"
                        ttl_class = "key-ttl" if ttl == -1 or ttl >= 0 else "key-expired"
                        st.markdown(f'**过期时间**: <span class="{ttl_class}">{ttl_str}</span>', unsafe_allow_html=True)

                    # 过期时间设置
                    with st.expander("设置过期时间"):
                        col1, col2 = st.columns(2)
                        with col1:
                            expiry_option = st.radio(
                                "过期选项",
                                ["设置过期时间", "永不过期"],
                                index=0 if ttl != -1 else 1
                            )

                        with col2:
                            if expiry_option == "设置过期时间":
                                expiry_seconds = st.number_input("过期时间(秒)", min_value=1,
                                                                 value=max(ttl, 60) if ttl > 0 else 3600)
                                if st.button("应用过期时间"):
                                    if st.session_state.redis_client.expire(selected_key, expiry_seconds):
                                        st.success("过期时间设置成功")
                                        st.rerun()
                                    else:
                                        st.error("过期时间设置失败")
                            else:
                                if st.button("移除过期时间"):
                                    if st.session_state.redis_client.persist(selected_key):
                                        st.success("已移除过期时间")
                                        st.rerun()
                                    else:
                                        st.error("移除过期时间失败")

                    # 显示键内容
                    st.markdown('<div class="data-container">', unsafe_allow_html=True)

                    if key_type == "string":
                        value = st.session_state.redis_client.get(selected_key)

                        # 尝试解析为JSON
                        try:
                            json_value = json.loads(value)
                            is_json = True
                        except:
                            is_json = False

                        # 显示选项
                        view_option = st.radio(
                            "查看方式",
                            ["原始文本", "JSON格式"] if is_json else ["原始文本"],
                            horizontal=True
                        )

                        if view_option == "原始文本":
                            st.text_area("值", value, height=200)
                        else:
                            st.json(json_value)

                        # 编辑功能
                        with st.expander("编辑值"):
                            new_value = st.text_area("新值", value, height=200)
                            if st.button("保存"):
                                if st.session_state.redis_client.set(selected_key, new_value):
                                    st.success("值已更新")
                                    st.rerun()
                                else:
                                    st.error("更新失败")

                    elif key_type == "list":
                        list_values = st.session_state.redis_client.lrange(selected_key, 0, -1)
                        list_length = len(list_values)

                        st.markdown(f"**列表长度**: {list_length}")

                        # 显示列表内容
                        for i, item in enumerate(list_values):
                            st.text_input(f"索引 {i}", item, key=f"list_{selected_key}_{i}")

                        # 列表操作
                        with st.expander("列表操作"):
                            operation = st.radio(
                                "操作类型",
                                ["添加元素", "移除元素", "更新元素"],
                                horizontal=True
                            )

                            if operation == "添加元素":
                                col1, col2 = st.columns(2)
                                with col1:
                                    position = st.radio("添加位置", ["左侧 (LPUSH)", "右侧 (RPUSH)"])
                                with col2:
                                    new_item = st.text_area("新元素值")

                                if st.button("添加"):
                                    if position == "左侧 (LPUSH)":
                                        if st.session_state.redis_client.lpush(selected_key, new_item):
                                            st.success("元素已添加到列表左侧")
                                            st.rerun()
                                        else:
                                            st.error("添加失败")
                                    else:
                                        if st.session_state.redis_client.rpush(selected_key, new_item):
                                            st.success("元素已添加到列表右侧")
                                            st.rerun()
                                        else:
                                            st.error("添加失败")

                            elif operation == "移除元素":
                                col1, col2 = st.columns(2)
                                with col1:
                                    remove_option = st.radio(
                                        "移除选项",
                                        ["按值移除", "按索引移除", "移除左侧第一个", "移除右侧第一个"]
                                    )

                                with col2:
                                    if remove_option == "按值移除":
                                        value_to_remove = st.text_input("要移除的值")
                                        count = st.number_input("移除数量 (0表示全部)", min_value=0, value=0)

                                        if st.button("移除"):
                                            removed = st.session_state.redis_client.lrem(selected_key, count,
                                                                                         value_to_remove)
                                            st.success(f"已移除 {removed} 个元素")
                                            st.rerun()

                                    elif remove_option == "按索引移除":
                                        index = st.number_input("索引", min_value=0,
                                                                max_value=list_length - 1 if list_length > 0 else 0)

                                        if st.button("移除"):
                                            # 使用LSET和LTRIM组合模拟按索引删除
                                            try:
                                                # 先将要删除的元素设为特殊值
                                                st.session_state.redis_client.lset(selected_key, index, "__DELETED__")
                                                # 然后移除这个特殊值
                                                st.session_state.redis_client.lrem(selected_key, 1, "__DELETED__")
                                                st.success(f"已移除索引 {index} 的元素")
                                                st.rerun()
                                            except:
                                                st.error("移除失败")

                                    elif remove_option == "移除左侧第一个":
                                        if st.button("移除"):
                                            removed = st.session_state.redis_client.lpop(selected_key)
                                            if removed:
                                                st.success(f"已移除左侧元素: {removed}")
                                                st.rerun()
                                            else:
                                                st.error("移除失败")

                                    else:  # 移除右侧第一个
                                        if st.button("移除"):
                                            removed = st.session_state.redis_client.rpop(selected_key)
                                            if removed:
                                                st.success(f"已移除右侧元素: {removed}")
                                                st.rerun()
                                            else:
                                                st.error("移除失败")

                            elif operation == "更新元素":
                                col1, col2 = st.columns(2)
                                with col1:
                                    index = st.number_input("索引", min_value=0,
                                                            max_value=list_length - 1 if list_length > 0 else 0)
                                with col2:
                                    if list_length > 0:
                                        current_value = st.session_state.redis_client.lindex(selected_key, index)
                                        new_value = st.text_area("新值", current_value)

                                        if st.button("更新"):
                                            try:
                                                st.session_state.redis_client.lset(selected_key, index, new_value)
                                                st.success(f"已更新索引 {index} 的元素")
                                                st.rerun()
                                            except:
                                                st.error("更新失败")
                                    else:
                                        st.warning("列表为空")

                    elif key_type == "hash":
                        hash_data = st.session_state.redis_client.hgetall(selected_key)

                        # 创建DataFrame
                        if hash_data:
                            hash_df = pd.DataFrame(list(hash_data.items()), columns=["字段", "值"])

                            # 显示哈希表内容
                            edited_hash = st.data_editor(
                                hash_df,
                                column_config={
                                    "字段": st.column_config.TextColumn("字段", width="medium"),
                                    "值": st.column_config.TextColumn("值", width="large"),
                                },
                                use_container_width=True,
                                num_rows="dynamic",
                                key="hash_editor"
                            )

                            # 保存编辑
                            if st.button("保存哈希表修改"):
                                # 获取原始数据和编辑后的数据
                                original_data = {row["字段"]: row["值"] for _, row in hash_df.iterrows()}
                                edited_data = {row["字段"]: row["值"] for _, row in edited_hash.iterrows() if
                                               row["字段"] and row["值"]}

                                # 找出要删除的字段
                                fields_to_delete = set(original_data.keys()) - set(edited_data.keys())

                                # 执行更新
                                if edited_data:
                                    st.session_state.redis_client.hset(selected_key, mapping=edited_data)

                                # 执行删除
                                if fields_to_delete:
                                    st.session_state.redis_client.hdel(selected_key, *fields_to_delete)

                                st.success("哈希表已更新")
                                st.rerun()
                        else:
                            st.info("哈希表为空")

                            # 添加新字段
                            with st.form("add_hash_field"):
                                col1, col2 = st.columns(2)
                                with col1:
                                    new_field = st.text_input("字段名")
                                with col2:
                                    new_value = st.text_area("字段值")

                                if st.form_submit_button("添加字段"):
                                    if new_field and new_value:
                                        st.session_state.redis_client.hset(selected_key, new_field, new_value)
                                        st.success(f"已添加字段: {new_field}")
                                        st.rerun()
                                    else:
                                        st.error("字段名和值不能为空")

                    elif key_type == "set":
                        set_members = st.session_state.redis_client.smembers(selected_key)

                        st.markdown(f"**集合大小**: {len(set_members)}")

                        # 显示集合内容
                        if set_members:
                            set_df = pd.DataFrame(list(set_members), columns=["值"])

                            st.dataframe(set_df, use_container_width=True)

                            # 集合操作
                            with st.expander("集合操作"):
                                operation = st.radio(
                                    "操作类型",
                                    ["添加元素", "移除元素"],
                                    horizontal=True
                                )

                                if operation == "添加元素":
                                    new_member = st.text_area("新元素值")

                                    if st.button("添加到集合"):
                                        if st.session_state.redis_client.sadd(selected_key, new_member):
                                            st.success("元素已添加到集合")
                                            st.rerun()
                                        else:
                                            st.warning("元素已存在于集合中")

                                elif operation == "移除元素":
                                    member_to_remove = st.selectbox("选择要移除的元素", list(set_members))

                                    if st.button("从集合中移除"):
                                        if st.session_state.redis_client.srem(selected_key, member_to_remove):
                                            st.success(f"已从集合中移除: {member_to_remove}")
                                            st.rerun()
                                        else:
                                            st.error("移除失败")
                        else:
                            st.info("集合为空")

                            # 添加新元素
                            new_member = st.text_area("新元素值")

                            if st.button("添加到集合"):
                                if new_member:
                                    if st.session_state.redis_client.sadd(selected_key, new_member):
                                        st.success("元素已添加到集合")
                                        st.rerun()
                                    else:
                                        st.warning("添加失败")
                                else:
                                    st.error("元素值不能为空")

                    elif key_type == "zset":
                        zset_members = st.session_state.redis_client.zrange(selected_key, 0, -1, withscores=True)

                        st.markdown(f"**有序集合大小**: {len(zset_members)}")

                        # 显示有序集合内容
                        if zset_members:
                            zset_data = [{"值": member, "分数": score} for member, score in zset_members]
                            zset_df = pd.DataFrame(zset_data)

                            st.dataframe(zset_df, use_container_width=True)

                            # 有序集合操作
                            with st.expander("有序集合操作"):
                                operation = st.radio(
                                    "操作类型",
                                    ["添加/更新元素", "移除元素", "按分数范围查询"],
                                    horizontal=True
                                )

                                if operation == "添加/更新元素":
                                    col1, col2 = st.columns(2)
                                    with col1:
                                        new_member = st.text_area("元素值")
                                    with col2:
                                        new_score = st.number_input("分数", value=1.0)

                                    if st.button("添加/更新"):
                                        if st.session_state.redis_client.zadd(selected_key, {new_member: new_score}):
                                            st.success("元素已添加/更新到有序集合")
                                            st.rerun()
                                        else:
                                            st.warning("添加/更新失败")

                                elif operation == "移除元素":
                                    member_to_remove = st.selectbox(
                                        "选择要移除的元素",
                                        [member for member, _ in zset_members]
                                    )

                                    if st.button("从有序集合中移除"):
                                        if st.session_state.redis_client.zrem(selected_key, member_to_remove):
                                            st.success(f"已从有序集合中移除: {member_to_remove}")
                                            st.rerun()
                                        else:
                                            st.error("移除失败")

                                elif operation == "按分数范围查询":
                                    col1, col2 = st.columns(2)
                                    with col1:
                                        min_score = st.number_input("最小分数", value=float('-inf'))
                                    with col2:
                                        max_score = st.number_input("最大分数", value=float('inf'))

                                    if st.button("查询"):
                                        range_members = st.session_state.redis_client.zrangebyscore(
                                            selected_key, min_score, max_score, withscores=True
                                        )

                                        if range_members:
                                            range_data = [{"值": member, "分数": score} for member, score in range_members]
                                            st.dataframe(pd.DataFrame(range_data), use_container_width=True)
                                            st.success(f"找到 {len(range_members)} 个元素")
                                        else:
                                            st.info("未找到符合条件的元素")
                        else:
                            st.info("有序集合为空")

                            # 添加新元素
                            col1, col2 = st.columns(2)
                            with col1:
                                new_member = st.text_area("元素值")
                            with col2:
                                new_score = st.number_input("分数", value=1.0)

                            if st.button("添加到有序集合"):
                                if new_member:
                                    if st.session_state.redis_client.zadd(selected_key, {new_member: new_score}):
                                        st.success("元素已添加到有序集合")
                                        st.rerun()
                                    else:
                                        st.warning("添加失败")
                                else:
                                    st.error("元素值不能为空")

                    st.markdown('</div>', unsafe_allow_html=True)

                    # 删除键
                    if st.button("🗑️ 删除该键", use_container_width=True):
                        if st.session_state.redis_client.delete(selected_key):
                            log_action("delete", f"删除key: {selected_key}")
                            st.success(f"已删除键: {selected_key}")
                            st.rerun()
                        else:
                            st.error("删除失败")
            else:
                st.info("没有找到匹配的键")

        except Exception as e:
            # 如果是WRONGTYPE错误，尝试提示key类型
            if "WRONGTYPE" in str(e) and len(args) > 0:
                key = args[0]
                try:
                    key_type = st.session_state.redis_client.type(key)
                    st.error(f"命令执行失败: {str(e)}。该键的类型为 `{key_type}`，请使用对应类型的命令。")
                except Exception as e2:
                    st.error(f"命令执行失败: {str(e)}")
            else:
                st.error(f"命令执行失败: {str(e)}")

        # 添加/更新键
        st.markdown('<h2 class="sub-header">📝 添加/更新键</h2>', unsafe_allow_html=True)

        with st.form("add_key_form"):
            col1, col2 = st.columns(2)

            with col1:
                new_key = st.text_input("键名")
                key_type = st.selectbox(
                    "数据类型",
                    ["string", "list", "hash", "set", "zset"]
                )

            with col2:
                ttl_enabled = st.checkbox("设置过期时间")
                if ttl_enabled:
                    ttl_seconds = st.number_input("过期时间(秒)", min_value=1, value=3600)

            # 根据选择的类型显示不同的输入字段
            if key_type == "string":
                new_value = st.text_area("值（文本或JSON）")

            elif key_type == "list":
                list_values = st.text_area("列表元素（每行一个元素）")

            elif key_type == "hash":
                hash_values = st.text_area("哈希表（JSON格式，如：{\"field1\": \"value1\", \"field2\": \"value2\"}）")

            elif key_type == "set":
                set_values = st.text_area("集合元素（每行一个元素）")

            elif key_type == "zset":
                zset_values = st.text_area("有序集合（每行一个元素和分数，格式：元素,分数）")

            submitted = st.form_submit_button("保存")

            if submitted:
                if not new_key:
                    st.error("键名不能为空")
                else:
                    try:
                        st.write(f"[调试] new_key: {new_key}")
                        st.write(f"[调试] key_type: {key_type}")
                        if key_type == "string":
                            st.write(f"[调试] new_value: {new_value}")
                            result = st.session_state.redis_client.set(new_key, new_value)
                            st.write(f"[调试] redis set 返回: {result}")
                        # ... 其他类型 ...
                        # 添加操作日志
                        log_action("add", f"添加key: {new_key}")
                        st.success(f"已保存键: {new_key}")
                        # st.rerun()  # 暂时注释掉，方便看调试信息
                    except Exception as e:
                        st.error(f"保存失败: {str(e)}")
                        st.error(traceback.format_exc())

        # 数据导入功能
        # 先显示导入结果提示
        if st.session_state.get("import_result"):
            st.success(st.session_state.import_result)
            del st.session_state.import_result
        with st.expander("数据导入"):
            uploaded_file = st.file_uploader("上传JSON文件", type=["json"])

            if uploaded_file is not None:
                try:
                    # 读取上传的JSON文件
                    content = uploaded_file.read().decode("utf-8")
                    data = json.loads(content)

                    # 显示数据预览
                    st.subheader("数据预览")
                    st.json(dict(list(data.items())[:3]))  # 只显示前3个键值对

                    # 导入选项
                    import_option = st.radio(
                        "导入方式",
                        ["自动检测格式", "作为哈希表导入", "使用标准格式(type/value)导入"],
                        index=0
                    )

                    # 添加导入按钮
                    if st.button("导入数据", key="import_data_button"):
                        success_count = 0
                        error_count = 0
                        skipped_count = 0

                        # 进度条
                        progress_bar = st.progress(0)
                        status_text = st.empty()

                        total_keys = len(data)

                        for i, (key, item) in enumerate(data.items()):
                            try:
                                # 更新进度
                                progress = int((i + 1) / total_keys * 100)
                                progress_bar.progress(progress)
                                status_text.text(f"正在导入: {i + 1}/{total_keys} ({progress}%)")

                                # 根据导入选项处理数据
                                if import_option == "作为哈希表导入":
                                    # 直接作为哈希表导入
                                    if isinstance(item, dict):
                                        st.session_state.redis_client.delete(key)
                                        # 转换所有 value 为字符串
                                        str_item = {k: str(v) for k, v in item.items()}
                                        st.session_state.redis_client.hset(key, mapping=str_item)
                                        success_count += 1
                                    else:
                                        st.warning(f"键 {key} 的值不是字典，无法作为哈希表导入")
                                        skipped_count += 1

                                elif import_option == "使用标准格式(type/value)导入":
                                    # 使用标准格式导入
                                    if isinstance(item, dict) and "type" in item and "value" in item:
                                        key_type = item.get("type")
                                        value = item.get("value")

                                        if key_type == "string":
                                            st.session_state.redis_client.set(key, value)
                                        elif key_type == "list":
                                            st.session_state.redis_client.delete(key)
                                            if value:
                                                st.session_state.redis_client.rpush(key, *value)
                                        elif key_type == "hash":
                                            st.session_state.redis_client.delete(key)
                                            if value:
                                                st.session_state.redis_client.hset(key, mapping=value)
                                        elif key_type == "set":
                                            st.session_state.redis_client.delete(key)
                                            if value:
                                                st.session_state.redis_client.sadd(key, *value)
                                        elif key_type == "zset":
                                            st.session_state.redis_client.delete(key)
                                            if value:
                                                for member, score in value:
                                                    st.session_state.redis_client.zadd(key, {member: score})
                                        success_count += 1
                                    else:
                                        st.warning(f"键 {key} 不符合标准格式(type/value)，已跳过")
                                        skipped_count += 1

                                else:  # 自动检测格式
                                    # 检测是否为标准格式
                                    if isinstance(item, dict) and "type" in item and "value" in item:
                                        # 使用标准格式导入
                                        key_type = item.get("type")
                                        value = item.get("value")

                                        if key_type == "string":
                                            st.session_state.redis_client.set(key, value)
                                        elif key_type == "list":
                                            st.session_state.redis_client.delete(key)
                                            if value:
                                                st.session_state.redis_client.rpush(key, *value)
                                        elif key_type == "hash":
                                            st.session_state.redis_client.delete(key)
                                            if value:
                                                st.session_state.redis_client.hset(key, mapping=value)
                                        elif key_type == "set":
                                            st.session_state.redis_client.delete(key)
                                            if value:
                                                st.session_state.redis_client.sadd(key, *value)
                                        elif key_type == "zset":
                                            st.session_state.redis_client.delete(key)
                                            if value:
                                                for member, score in value:
                                                    st.session_state.redis_client.zadd(key, {member: score})
                                    else:
                                        # 作为哈希表导入
                                        if isinstance(item, dict):
                                            st.session_state.redis_client.delete(key)
                                            # 转换所有 value 为字符串
                                            str_item = {k: str(v) for k, v in item.items()}
                                            st.session_state.redis_client.hset(key, mapping=str_item)
                                        else:
                                            # 作为字符串导入
                                            st.session_state.redis_client.set(key, json.dumps(item))

                                    success_count += 1

                            except Exception as e:
                                error_count += 1
                                st.error(f"导入键 {key} 失败: {str(e)}")

                        # 显示导入结果
                        if success_count > 0:
                            st.session_state.import_result = f"🎉 数据导入已完成！成功 {success_count} 个键, 失败 {error_count} 个键, 跳过 {skipped_count} 个键"
                            st.rerun()
                        else:
                            st.session_state.import_result = "导入失败: 没有成功导入任何键"
                            st.rerun()

                except Exception as e:
                    st.error(f"解析JSON文件失败: {str(e)}")

        # Redis命令执行
        with st.expander("Redis命令执行"):
            command = st.text_area("输入Redis命令", placeholder="例如: GET mykey, HGETALL myhash")

            if st.button("执行命令"):
                if command:
                    try:
                        # 解析命令
                        cmd_parts = command.strip().split()
                        cmd = cmd_parts[0].upper()
                        args = cmd_parts[1:]

                        # 执行命令
                        result = st.session_state.redis_client.execute_command(cmd, *args)

                        # 显示结果
                        st.success("命令执行成功")
                        st.code(str(result))
                    except Exception as e:
                        # 如果是WRONGTYPE错误，尝试提示key类型
                        if "WRONGTYPE" in str(e) and len(args) > 0:
                            key = args[0]
                            try:
                                key_type = st.session_state.redis_client.type(key)
                                st.error(f"命令执行失败: {str(e)}。该键的类型为 `{key_type}`，请使用对应类型的命令。")
                            except Exception as e2:
                                st.error(f"命令执行失败: {str(e)}")
                        else:
                            st.error(f"命令执行失败: {str(e)}")
                else:
                    st.warning("请输入要执行的命令")

        with st.expander("常用命令帮助"):
            st.markdown("""
- `GET key`：获取字符串键的值
- `SET key value`：设置字符串键的值
- `HGETALL key`：获取哈希表所有字段和值
- `LRANGE key 0 -1`：获取列表所有元素
- `SADD key member`：向集合添加元素
- `SMEMBERS key`：获取集合所有成员
- `ZADD key score member`：向有序集合添加元素
- `ZREVRANGE key 0 -1 WITHSCORES`：获取有序集合所有成员及分数
- `DEL key`：删除键
- `EXPIRE key seconds`：设置键过期时间
- `TTL key`：查看键剩余过期时间
- `KEYS pattern`：按模式查找键
    """)

        # 慢查询分析
        st.markdown("#### Redis 慢查询日志（SLOWLOG）：展示最近慢命令的执行详情。")
        slowlogs = st.session_state.redis_client.slowlog_get()
        df = pd.DataFrame(slowlogs)
        st.dataframe(df)

        # 内存分析
        st.markdown("#### 内存分析：统计各类型 key 的内存占用。")
        memory_data = []
        for key in st.session_state.redis_client.scan_iter("*"):
            size = st.session_state.redis_client.memory_usage(key)
            key_type = st.session_state.redis_client.type(key)
            memory_data.append({"key": key, "type": key_type, "size": size})
        df = pd.DataFrame(memory_data)
        df_grouped = df.groupby("type", as_index=False)["size"].sum()
        chart = alt.Chart(df_grouped).mark_bar().encode(
            x=alt.X('type:N', title='数据类型'),
            y=alt.Y('size:Q', title='内存占用（字节）'),
            color=alt.Color('type:N', legend=alt.Legend(title="数据类型"))
        ).properties(
            width=600,
            height=400,
            title="不同数据类型的内存占用"
        )
        st.altair_chart(chart, use_container_width=True)

        # 即将过期的键
        st.markdown("#### 即将过期的 key：列出1小时内即将过期的 key，便于提前关注和处理。")
        expiring_keys = []
        for key in st.session_state.redis_client.scan_iter("*"):
            ttl = st.session_state.redis_client.ttl(key)
            if 0 < ttl < 3600:  # 1小时内过期
                expiring_keys.append({"key": key, "ttl": ttl})
        df = pd.DataFrame(expiring_keys)
        st.dataframe(df)
    except Exception as e:
        st.error(f"命令执行失败: {str(e)}")
